Etapa 1: carga de datos y preprocesamiento
El archivo que se adjunta consiste en un corpus de unas 7.000
noticias scrapeadas entre julio y septiembre de 2019 de los siguientes
medios de circulación nacional:
Télam La Nación Clarín Perfil Infobae MinutoUno Página 12
Constituye una muestra aleatoria del corpus construido por Florencia
Piñeyrúa para su tesina de grado “Procesamiento del lenguaje natural
aplicado al estudio de tópicos de noticias de seguridad en Argentina:
julio a septiembre 2019”. Una exposición más concentrada de sus
resultados puede encontrarse en el siguiente artículo.
El corpus contiene las siguientes variables:
id : identificador de cada documento url : link a la noticia original
fecha : fecha de publicación anio : año de publicación mes : mes de
publicación dia : dia de publicación medio : medio en el que fue
publicado orientacion: clasificación -provisoria- de los medios según su
línea editorial predominante (más conservador, más progresista, neutral)
titulo texto
Carga dataset
corpus_base <- read_csv("M5_corpus_medios.csv")
# En este apartado vemos cuántas noticias aporta cada medio al corpus y calculamos la proporción sobre el total.
corpus_base %>%
group_by(medio) %>%
summarise(n=n()) %>%
mutate(
total = sum(n),
prop = n/total*100
) %>%
ungroup() %>%
select(medio, n, prop) %>%
arrange(desc(n))
Normalización y tokenización
corpus_tidy <- corpus_base %>%
mutate(texto= stringi::stri_trans_general(texto, "Latin-ASCII"),
titulo = stringi::stri_trans_general(titulo, "Latin-ASCII")) %>%
mutate(texto = str_replace_all(texto, '[[:digit:]]+', '')) %>%
unnest_tokens(word, texto, to_lower = TRUE)
# En este este código tomamos el corpus de texto "corpus_base", lo normalizamos convirtiendo el texto y el título a caracteres ASCII, eliminamos los dígitos del texto y finalmente los dividimos en tokens para su posterior análisis.
Eliminar stopwords
stop_words_1 <- read_csv('https://raw.githubusercontent.com/Alir3z4/stop-words/master/spanish.txt', col_names=FALSE) %>%
rename(word = X1) %>%
mutate(word = stringi::stri_trans_general(word, "Latin-ASCII"))
Rows: 608 Columns: 1── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# En este código leemos un archivo CSV de stop words en español ubicado en la URL especificada, renombramos la columna a "word" y luego transformamos las palabras en la columna para asegurarnos de que estén en formato ASCII. El resultado final es un dataframe llamado "stop_words_1" que contiene la lista de stopwords normalizadas.
stop_words_2 <- read_csv("z_stopwords.txt", col_names = FALSE) %>%
rename(word = X1) %>%
mutate(word=stringi::stri_trans_general(word, "Latin-ASCII"))
Rows: 732 Columns: 1── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): X1
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
# Ídem pero el archivo CSV llamado "z_stopwords.txt" se carga desde la carpeta.
stop_words_full <- stop_words_1 %>%
bind_rows(stop_words_2) %>%
distinct()
# En este paso combinamos los dos dataframes de stopwords, eliminamos las filas duplicadas y guardamos el resultado en un nuevo dataframe llamado "stop_words_full". Este dataframe contiene la lista completa y única de stopwords normalizadas.
corpus_tidy <- corpus_tidy %>%
anti_join(stop_words_full)
Joining with `by = join_by(word)`
# En este código eliminamos las stopwords del corpus de texto "corpus_tidy" utilizando la lista de stopwords contenida en el dataframe "stop_words_full". Este paso en el procesamiento de texto es necesario para eliminar palabras que no aportan significado para el análisis posterior.
Corrección “años”
corpus_tidy <- corpus_tidy %>%
mutate(word = case_when(
word == 'ano' ~ 'anio',
word == 'anos' ~ 'anio',
TRUE ~ word
))
PRUEBITASSS
t <- words_tidy_test %>% mutate(tf = n/total) %>%
ggplot(aes(tf)) +
geom_histogram(show.legend = FALSE) +
coord_flip()+
#xlim(NA, 0.0002) +
facet_wrap(~medio) +
theme_classic()+
theme(plot.title = element_text(hjust= 0.5),
axis.title = element_blank(),
axis.ticks.x = element_blank(),
text = element_text(family = "Courier"))
plotly(t)
Storing 'username' as the environment variable 'plotly_username'
Error in Sys.setenv(plotly_username = username) :
wrong length for argument
En esta visualización podemos interactuar para identificar la
cantidad de términos por tf:
tf_viz <- words_tidy %>% mutate(tf = n/total) %>%
ggplot(aes(tf)) +
geom_histogram(show.legend = FALSE) +
coord_flip()+
xlim(NA, 0.0002) +
facet_wrap(~medio) +
theme_classic()+
theme(plot.title = element_text(hjust= 0.5),
axis.title = element_blank(),
axis.ticks.x = element_blank(),
text = element_text(family = "Courier"))
ggplotly(tf_viz)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.Warning: Removed 6859 rows containing non-finite values (`stat_bin()`).
Vemos que para los medios más masivos se cumple la Ley de Zipf: pocas
palabras ocurren muchas veces. Los corpus de Crónica, Minuto Uno y Télam
tienen menos términos que los demás y la tf tiene un rango menor (uso de
palabras mejor distribuido).
Term Frequency - Inverse Document Frequency (TF-IDF)
En esta etapa complementamos el análisis con las métricas de idf y
tf_idf, para obtener una medida más completa de la importancia de los
términos en el corpus de documentos.
tf_idf <- words_tidy %>%
bind_tf_idf(word, medio, n)
tf_idf %>% select(-total) %>%
arrange(desc(tf_idf))
# En este código calculamos las métricas tf, idf y tf_idf para todos los términos del corpus
Creamos tres dataset, uno por métrica, con el top ten de términos
para pasarle a la función de visualización.
idf_10 <- tf_idf %>%
group_by(medio) %>%
arrange(desc(idf)) %>%
slice_head(n = 10) %>%
select(medio, word, idf) %>%
rename(n = idf) %>%
mutate(n = log(n + 1) * 100)
Error in tf_idf %>% group_by(medio) %>% arrange(desc(idf)) %>% slice_head(n = 10) %>% :
could not find function "%>%<-"
Pasamos cada uno de los top ten de términos por la función de
graficado para visualizar los términos más importantes según cada
métrica.
crear_graf_words(tf_10)

crear_graf_words(idf_10)

crear_graf_words(tf_idf_10)

Análisis preliminar de TF-IDF:
En primer lugar identificamos la necesidad de agregar palabras al
listado de stopwords. Entendemos que no aportan información sobre el
contenido u orientación de las noticias y a su vez son únicos para
algunos medios y eso genera un alto tf_idf.
Ejemplos: jpe, ap, emj, cronica.com.ar, fvazquez, cronicavirales, hd,
pemex, gt, afv, jpg, minutouno.com, ambito.com, ivanovich, loading,
paginai, protected, lxs, email, r.c,cp, fel,l.l,d.s, ea, f.f, f.d.s, fh,
a.g, pct, telam.la, nacional.el
Podemos identificar “firmas” de periodistas (nombres de usuario o
siglas) que tampoco son informativas sobre el contenido del
corpus.
Etapa 1.2: Ampliación stopwords y reprocesamiento de la base
El análisis realizado nos permitió encontrar numerosas palabras
incluidas en el corpus que creemos son producto del scrapping. Previo a
la modelización para identificar tópicos creemos pertinente ampliar el
listado de stopwords y repetir el preprocesamiento de los datos.
Para ampliar el listado de stopwords partimos del listado de palabras
con sus métricas de tf e idf.
# Mediante el siguiente código eliminamos palabras que incluyen puntos y parecen producto de scrapping web.
nuevas_stopwords_1 <- tf_idf %>%
select(word) %>%
filter(grepl('\\.', word)) %>%
pull(word)
# En este paso eliminamos palabras de dos caracteres, salvo contadas excepciones que tienen sentido.
nuevas_stopwords_2 <- tf_idf %>%
select(word) %>%
filter(str_length(word) == 2 & !(word %in% c('pj', 'fe', 'dt', 'tv', 'km', 'cv', 'dr', 'it', 'dj', 'ux'))) %>%
pull(word)
# Este código elimina palabras de tres caracteres. Parece haber más siglas y palabras cortas que tienen sentido pero, dada la gran cantidad de stopwords de 3 caracteres, hay motivos para incluir este paso.
nuevas_stopwords_3 <- tf_idf %>%
select(word) %>%
filter(str_length(word) == 3 & !(word %in% c('san', 'mil', 'ley', 'afp', 'sur', 'fmi', 'usd', 'rio', 'gol', 'paz', 'mar', 'oro', 'red', 'luz', 'voz' , 'rol', 'sol', 'gas', 'pie', 'par', 'pro', 'via', 'onu', 'ypf', 'iva', 'afa', 'pbi', 'bar', 'cfk', 'eje', 'rey', 'atp', 'don', 'fbi', 'gay', 'psg', 'uba', 'ucr', 'ceo', 'ong', 'fed', 'ojo', 'dea', 'uva', 'cgt', 'ufi', 'app', 'gil', 'vih', 'nba', 'bbc', 'evo', 'hip', 'hop', 'fox', 'nbc', 'rap', 'adn', 'ala', 'eva', 'pan', 'zen', 'afv', 'cck', 'eco', 'oca', 'tio', 'cnn', 'cia', "dni", "uia", "fdt", "uif", "hbo", "mls", "oea", "mep", "gnc", "auh", "che", "oil", "gen", "agn", "fpv", "lam", "pib", "cne","duo", "vox", "dow", "uca", "dnu", "pez", "pfa", "pdt", "fda", "fci", "oms", "psa", "feo", "cta", "ego",
"faa", "ute", "ate", "lio", "fpt", "suv", "gba", "izq", "aro", "smn", "tnt", "uco", "ipc", "saa", "tmz", "ccl", "gel", "vip", "esi", "res", "kun", "tsj", "afi", "pts", "cnh", "ajo", "acv", "bmw", "bus", "gps", "ile", "ios", "unc", "zoo", "jup", "tos", "unl", "upl", "zeo", "ave", "mte", "mpn", "apn", "mao", "pba", "sms", "cnv", "mdz", "fol", "iso"))) %>%
pull(word)
nuevas_stopwords <- data.frame(word = nuevas_stopwords_1) %>%
bind_rows(data.frame(word = nuevas_stopwords_2)) %>%
bind_rows(data.frame(word = nuevas_stopwords_3))
stop_words_full_v2 <- stop_words_full %>%
bind_rows(tibble(word = c('embed', 'anio', 'ano', 'anos', 'gusta', 'twitter', 'facebook', 'comentar', 'fuente', 'whatsapp', 'guardar', 'compartir', 'mail', 'loading', 'email', 'paginai', 'eltrece', 'infobae', 'http', 'https', 'attribute', 'find_all', 'nonetype', 'object', 'read' ))) %>%
bind_rows(nuevas_stopwords)
A partir de la nueva lista de stopwords, recreamos el corpus en
formato tidy y también el dataframe en formato medio/término/n para
continuar con el análisis de métricas.
corpus_tidy_v2 <- corpus_tidy %>%
anti_join(stop_words_full_v2)
Joining with `by = join_by(word)`
words_tidy_v2 <- corpus_tidy_v2 %>%
group_by(medio, word) %>%
summarise(n=n())
`summarise()` has grouped output by 'medio'. You can override using the `.groups` argument.
total_words_v2 <- words_tidy_v2 %>%
group_by(medio) %>%
summarize(total = sum(n))
words_tidy_v2 <- words_tidy_v2 %>%
left_join(total_words_v2) %>%
ungroup() %>%
arrange(desc(n))
Joining with `by = join_by(medio)`
Revisión de métricas
Creamos el gráfico con la distribución de términos según su tf para
identificar variaciones respecto a la v1.
tf_viz_v2 <- words_tidy_v2 %>% mutate(tf = n/total) %>%
ggplot(aes(tf)) +
geom_histogram(show.legend = FALSE) +
coord_flip()+
xlim(NA, 0.0002) +
facet_wrap(~medio) +
theme_classic()+
theme(plot.title = element_text(hjust= 0.5),
axis.title = element_blank(),
axis.ticks.x = element_blank(),
text = element_text(family = "Courier"))
ggplotly(tf_viz_v2)
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.Warning: Removed 6945 rows containing non-finite values (`stat_bin()`).
tf_idf_v2 <- words_tidy_v2 %>%
bind_tf_idf(word, medio, n)
Creamos los dataset necesarios para visualizar los principales
términos por métrica y por medio, buscando diferencias respecto a la
v1.
tf_10_v2 <- tf_idf_v2 %>%
group_by(medio) %>%
arrange(desc(tf)) %>%
slice_head(n = 10) %>%
select(medio, word, tf) %>%
rename(n = tf) %>%
mutate(n = log(n + 1) * 100) %>%
mutate(n = round(n, 4))
idf_10_v2 <- tf_idf_v2 %>%
group_by(medio) %>%
arrange(desc(idf)) %>%
slice_head(n = 10) %>%
select(medio, word, idf) %>%
rename(n = idf) %>%
mutate(n = log(n + 1) * 100) %>%
mutate(n = round(n, 4))
tf_idf_10_v2 <- tf_idf_v2 %>%
group_by(medio) %>%
arrange(desc(idf)) %>%
slice_head(n = 10) %>%
select(medio, word, tf_idf) %>%
rename(n = tf_idf) %>%
mutate(n = log(n + 1) * 100) %>%
mutate(n = round(n, 4))

crear_graf_words(idf_10_v2)

crear_graf_words(tf_idf_10_v2)

Análisis final de TF-IDF:
En todos los medios, excepto en La Nación que no la incluía,
logró eliminarse la palabra “anio” que figuraba en el primer lugar en
términos de frecuencia. Además, en los casos de Crónica y Minuto 1, se
eliminó otra palabra adicional incorporando, de esa manera, dos nuevas
al top diez.
El caso de La Nación ya se muestra normalizado, sin las stopwords
que aparecían producto del scraping.
| ## Modelado de tópicos: LDA |
| ¿Cuáles son los tópicos principales en el corpus?
¿Pueden evidenciar diferencias en cada uno de los medios? Explicar qué
método se utilizó para responder la pregunta, cuáles son los supuestos
del mismo. Generar las visualizaciones más adecuadas para responder a
las preguntas. |
| Para implementar un modelado de temas con LDA
necesitamos construir una matriz DTM. |
| # A PARTIR DE ACÁ dejo las dos pruebas para que puedas
comparar. Si usamos la matriz término-frecuencia por MEDIO, la detección
de tópicos da cualquier cosa. Si usamos la matriz término-frecuencia por
NOTICIA el resultado tiene más sentido. No estoy segura como se explica,
quizás podemos encontrar alguna justificación. |
| –> Nos quedamos con la versión 2 (por id) |
|
|
|
| ```r disc_dtm_1 <- words_tidy_v2 %>%
cast_dtm(medio, word, n) |
| ``` |
|
|
|
|
|
|
r para_disc_dtm <- corpus_tidy_v2 %>% group_by(id, word) %>% summarise(n=n()) |
|
|
`summarise()` has grouped output by 'id'. You can override using the `.groups` argument. |
|
|
| ```r # creo un conteo de palabras por noticia, no por
medio |
| disc_dtm_2 <- para_disc_dtm %>% cast_dtm(id,
word, n) |
| ``` |
|
|
|
|
|
|
| ```r lda_orig_5 <- LDA(disc_dtm_1, k = 5, control =
list(seed = 1234)) # k es el número de tópicos a identificar |
| ap_topics <- tidy(lda_orig_5, matrix = “beta”) # la
función tidy convierte el modelo a un tópico-término por fila |
| ap_topics %>% mutate(beta = round(100*beta,6))
``` |
|
|
|
|
|
| ```r |
| ap_top_terms <- ap_topics %>% group_by(topic)
%>% slice_max(beta, n = 15) %>% ungroup() %>% arrange(topic,
-beta) |
| ap_top_terms %>% mutate(term = reorder_within(term,
beta, topic)) %>% ggplot(aes(beta, term, fill = factor(topic))) +
geom_col(show.legend = FALSE) + facet_wrap(~ topic, scales=‘free_y’) +
scale_y_reordered() + theme_minimal() ``` |
|
|
 |
|
|
|
|
|
|
|
| Pareciera que los cinco tópicos no tienen un sentido
tan definido. Entonces, cabe la posibilidad de que haga falta utilizar
un número de tópicos más elevado. |
| A su vez, la visualización anterior muestra que algunas
palabras como… son comunes a más de un tema. Es decir, que los tópicos
identificados tienen cierta superposición en términos de palabras.Como
alternativa, podríamos considerar los términos que tuvieran la mayor
diferencia en β entre todos los temas. |
|
|
|
| ```r |
| # ESTO NO SÉ SI ESTÁ BIEN APLICADO, EN LA NOTEBOOK EL
CASO ERA ALGO DISTINTO. SE CALCULABA SOBRE LOS TÓPICOS MÁS DEFINIDOS,
PERO EN NUESTRO CASO NINGUNO ES DEFINIDO. |
| # Con este código calculamos el logaritmo de la
proporción de cada tópico respecto al tópico de referencia para los
cinco tópicos del conjunto de datos. Cada columna log_ratio representa
la diferencia logarítmica entre los tópicos adyacentes. Esto proporciona
información sobre cómo cambia la contribución relativa de cada tópico en
comparación con su tópico adyacente. |
| beta_wide3 <- ap_topics %>% mutate(topic =
paste0(“topic”, topic)) %>% pivot_wider(names_from = topic,
values_from = beta) %>% filter(topic1 > 0.002 | topic2 > 0.002
| topic3 > 0.002 | topic4 > 0.002 | topic5 > 0.002) %>% #
Filtro para eliminar valores muy bajos mutate(log_ratio1_2 = log2(topic2
/ topic1), log_ratio2_3 = log2(topic3 / topic2), log_ratio3_4 =
log2(topic4 / topic3), log_ratio4_5 = log2(topic5 / topic4),
log_ratio5_1 = log2(topic1 / topic5)) |
| beta_wide3 ``` |
|
|
|
|
|
|
r beta_wide3 %>% ggplot(aes(x=reorder(term,log_ratio1_2) , y=log_ratio1_2)) + geom_col() + coord_flip() + labs(x='Término', y='Log2 ratio topic3/topic2') + theme_minimal() |
|
|
|
|
|
|
r beta_wide3 %>% ggplot(aes(x=reorder(term,log_ratio2_3) , y=log_ratio2_3)) + geom_col() + coord_flip() + labs(x='Término', y='Log2 ratio topic2/topic1') + theme_minimal() |
|
|
|
|
|
|
r beta_wide3 %>% ggplot(aes(x=reorder(term,log_ratio3_4) , y=log_ratio3_4)) + geom_col() + coord_flip() + labs(x='Término', y='Log2 ratio topic4/topic3') + theme_minimal() |
|
|
|
|
|
|
r beta_wide3 %>% ggplot(aes(x=reorder(term,log_ratio4_5) , y=log_ratio4_5)) + geom_col() + coord_flip() + labs(x='Término', y='Log2 ratio topic5/topic4') + theme_minimal() |
|
|
|
|
|
|
r beta_wide3 %>% ggplot(aes(x=reorder(term, log_ratio5_1) , y= log_ratio5_1)) + geom_col() + coord_flip() + labs(x='Término', y='Log2 ratio topic1/topic5') + theme_minimal() |
|
|
|
| ## Modelado de tópicos: STM |
| Para implementar un modelado de temas con STM
necesitamos construir una matriz DFM. |
|
|
|
| ```r disc_dfm <- words_tidy_v2 %>%
cast_dfm(medio, word, n) |
| disc_dfm ``` |
|
|
|
A continuación, seleccionar las noticias vinculadas a algún tópico
relevante (por ejemplo, “Elecciones”) y construir un clasificador para
predecir la orientación del diario. Utilizar alguno de los modelos de
clasificación vistos a lo largo de al Diplomatura (regresión logística,
random forest, etc.). Utilizar como features el “Spanish Billion Word
Corpus and Embeddings”, analizado en clase (pueden descargar el
embedding en formato .bin del link). ¿Qué resultados arroja el modelo?
¿Es posible mediante el texto de las noticias conocer la línea editorial
del diario? Generar las visualizaciones y tablas correspondientes para
una correcta evaluación del modelo.
LS0tCnRpdGxlOiAiVHJhYmFqbyBGaW5hbCBEaXBsb21hQ1NDIC0gT3BjacOzbiAyIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKIyMgQ2FyZ2EgZGUgbGlicmVyw61hcyBhIHV0aWxpemFyCgpgYGB7ciBsaWJyYXJ5fQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5dGV4dCkKbGlicmFyeSh0bSkKbGlicmFyeSh0ZXh0c3RlbSkKbGlicmFyeShwYXRjaHdvcmspCmxpYnJhcnkod29yZGNsb3VkKSAKbGlicmFyeShwbG90bHkpCmxpYnJhcnkodG9waWNtb2RlbHMpCmxpYnJhcnkodGljdG9jKQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkocmVzaGFwZTIpCmBgYAoKIyMgRXRhcGEgMTogY2FyZ2EgZGUgZGF0b3MgeSBwcmVwcm9jZXNhbWllbnRvCgpFbCBhcmNoaXZvIHF1ZSBzZSBhZGp1bnRhIGNvbnNpc3RlIGVuIHVuIGNvcnB1cyBkZSB1bmFzIDcuMDAwIG5vdGljaWFzIHNjcmFwZWFkYXMgZW50cmUganVsaW8geSBzZXB0aWVtYnJlIGRlIDIwMTkgZGUgbG9zIHNpZ3VpZW50ZXMgbWVkaW9zIGRlIGNpcmN1bGFjacOzbiBuYWNpb25hbDoKClTDqWxhbSBMYSBOYWNpw7NuIENsYXLDrW4gUGVyZmlsIEluZm9iYWUgTWludXRvVW5vIFDDoWdpbmEgMTIKCkNvbnN0aXR1eWUgdW5hIG11ZXN0cmEgYWxlYXRvcmlhIGRlbCBjb3JwdXMgY29uc3RydWlkbyBwb3IgRmxvcmVuY2lhIFBpw7FleXLDumEgcGFyYSBzdSB0ZXNpbmEgZGUgZ3JhZG8g4oCcUHJvY2VzYW1pZW50byBkZWwgbGVuZ3VhamUgbmF0dXJhbCBhcGxpY2FkbyBhbCBlc3R1ZGlvIGRlIHTDs3BpY29zIGRlIG5vdGljaWFzIGRlIHNlZ3VyaWRhZCBlbiBBcmdlbnRpbmE6IGp1bGlvIGEgc2VwdGllbWJyZSAyMDE54oCdLiBVbmEgZXhwb3NpY2nDs24gbcOhcyBjb25jZW50cmFkYSBkZSBzdXMgcmVzdWx0YWRvcyBwdWVkZSBlbmNvbnRyYXJzZSBlbiBlbCBzaWd1aWVudGUgYXJ0w61jdWxvLgoKRWwgY29ycHVzIGNvbnRpZW5lIGxhcyBzaWd1aWVudGVzIHZhcmlhYmxlczoKCmlkIDogaWRlbnRpZmljYWRvciBkZSBjYWRhIGRvY3VtZW50bwp1cmwgOiBsaW5rIGEgbGEgbm90aWNpYSBvcmlnaW5hbApmZWNoYSA6IGZlY2hhIGRlIHB1YmxpY2FjacOzbgphbmlvIDogYcOxbyBkZSBwdWJsaWNhY2nDs24KbWVzIDogbWVzIGRlIHB1YmxpY2FjacOzbgpkaWEgOiBkaWEgZGUgcHVibGljYWNpw7NuCm1lZGlvIDogbWVkaW8gZW4gZWwgcXVlIGZ1ZSBwdWJsaWNhZG8Kb3JpZW50YWNpb246IGNsYXNpZmljYWNpw7NuIC1wcm92aXNvcmlhLSBkZSBsb3MgbWVkaW9zIHNlZ8O6biBzdSBsw61uZWEKZWRpdG9yaWFsIHByZWRvbWluYW50ZSAobcOhcyBjb25zZXJ2YWRvciwgbcOhcyBwcm9ncmVzaXN0YSwgbmV1dHJhbCkKdGl0dWxvCnRleHRvCgojIyMgQ2FyZ2EgZGF0YXNldAoKYGBge3IgcmVhZGNzdn0KY29ycHVzX2Jhc2UgPC0gcmVhZF9jc3YoIk01X2NvcnB1c19tZWRpb3MuY3N2IikKYGBgCgpgYGB7ciBhbmFsaXNpc19iYXNlfQojIEVuIGVzdGUgYXBhcnRhZG8gdmVtb3MgY3XDoW50YXMgbm90aWNpYXMgYXBvcnRhIGNhZGEgbWVkaW8gYWwgY29ycHVzIHkgY2FsY3VsYW1vcyBsYSBwcm9wb3JjacOzbiBzb2JyZSBlbCB0b3RhbC4KCiAgICBjb3JwdXNfYmFzZSAlPiUKICAgICAgICBncm91cF9ieShtZWRpbykgJT4lCiAgICAgICAgc3VtbWFyaXNlKG49bigpKSAlPiUKICAgICAgICBtdXRhdGUoCiAgICAgICAgICAgICAgICB0b3RhbCA9IHN1bShuKSwKICAgICAgICAgICAgICAgIHByb3AgPSBuL3RvdGFsKjEwMAogICAgICAgICAgICAgICAgKSAlPiUKICAgICAgICB1bmdyb3VwKCkgJT4lCiAgICAgICAgc2VsZWN0KG1lZGlvLCBuLCBwcm9wKSAlPiUgCiAgICAgICAgYXJyYW5nZShkZXNjKG4pKQpgYGAKCiMjIyBOb3JtYWxpemFjacOzbiB5IHRva2VuaXphY2nDs24KCmBgYHtyIHRva2Vuc30KY29ycHVzX3RpZHkgPC0gY29ycHVzX2Jhc2UgJT4lIAogICAgbXV0YXRlKHRleHRvPSBzdHJpbmdpOjpzdHJpX3RyYW5zX2dlbmVyYWwodGV4dG8sICJMYXRpbi1BU0NJSSIpLAogICAgICAgICB0aXR1bG8gPSBzdHJpbmdpOjpzdHJpX3RyYW5zX2dlbmVyYWwodGl0dWxvLCAiTGF0aW4tQVNDSUkiKSkgJT4lIAogICAgbXV0YXRlKHRleHRvID0gc3RyX3JlcGxhY2VfYWxsKHRleHRvLCAnW1s6ZGlnaXQ6XV0rJywgJycpKSAlPiUgCiAgICB1bm5lc3RfdG9rZW5zKHdvcmQsIHRleHRvLCB0b19sb3dlciA9IFRSVUUpCgojIEVuIGVzdGUgZXN0ZSBjw7NkaWdvIHRvbWFtb3MgZWwgY29ycHVzIGRlIHRleHRvICJjb3JwdXNfYmFzZSIsIGxvIG5vcm1hbGl6YW1vcyBjb252aXJ0aWVuZG8gZWwgdGV4dG8geSBlbCB0w610dWxvIGEgY2FyYWN0ZXJlcyBBU0NJSSwgZWxpbWluYW1vcyBsb3MgZMOtZ2l0b3MgZGVsIHRleHRvIHkgZmluYWxtZW50ZSBsb3MgZGl2aWRpbW9zIGVuIHRva2VucyBwYXJhIHN1IHBvc3RlcmlvciBhbsOhbGlzaXMuCmBgYAoKIyMjIEVsaW1pbmFyIHN0b3B3b3JkcwoKYGBge3Igc3RvcHdvcmRzfQpzdG9wX3dvcmRzXzEgPC0gcmVhZF9jc3YoJ2h0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9BbGlyM3o0L3N0b3Atd29yZHMvbWFzdGVyL3NwYW5pc2gudHh0JywgY29sX25hbWVzPUZBTFNFKSAlPiUKICAgICAgICByZW5hbWUod29yZCA9IFgxKSAlPiUKICAgICAgICBtdXRhdGUod29yZCA9IHN0cmluZ2k6OnN0cmlfdHJhbnNfZ2VuZXJhbCh3b3JkLCAiTGF0aW4tQVNDSUkiKSkKCiMgRW4gZXN0ZSBjw7NkaWdvIGxlZW1vcyB1biBhcmNoaXZvIENTViBkZSBzdG9wIHdvcmRzIGVuIGVzcGHDsW9sIHViaWNhZG8gZW4gbGEgVVJMIGVzcGVjaWZpY2FkYSwgcmVub21icmFtb3MgbGEgY29sdW1uYSBhICJ3b3JkIiB5IGx1ZWdvIHRyYW5zZm9ybWFtb3MgbGFzIHBhbGFicmFzIGVuIGxhIGNvbHVtbmEgcGFyYSBhc2VndXJhcm5vcyBkZSBxdWUgZXN0w6luIGVuIGZvcm1hdG8gQVNDSUkuIEVsIHJlc3VsdGFkbyBmaW5hbCBlcyB1biBkYXRhZnJhbWUgbGxhbWFkbyAic3RvcF93b3Jkc18xIiBxdWUgY29udGllbmUgbGEgbGlzdGEgZGUgc3RvcHdvcmRzIG5vcm1hbGl6YWRhcy4KCnN0b3Bfd29yZHNfMiA8LSByZWFkX2Nzdigiel9zdG9wd29yZHMudHh0IiwgY29sX25hbWVzID0gRkFMU0UpICU+JSAKICByZW5hbWUod29yZCA9IFgxKSAlPiUgCiAgIG11dGF0ZSh3b3JkPXN0cmluZ2k6OnN0cmlfdHJhbnNfZ2VuZXJhbCh3b3JkLCAiTGF0aW4tQVNDSUkiKSkKCiMgw41kZW0gcGVybyBlbCBhcmNoaXZvIENTViBsbGFtYWRvICJ6X3N0b3B3b3Jkcy50eHQiIHNlIGNhcmdhIGRlc2RlIGxhIGNhcnBldGEuCgpzdG9wX3dvcmRzX2Z1bGwgPC0gc3RvcF93b3Jkc18xICU+JSAKICBiaW5kX3Jvd3Moc3RvcF93b3Jkc18yKSAlPiUgCiAgZGlzdGluY3QoKQoKIyBFbiBlc3RlIHBhc28gY29tYmluYW1vcyBsb3MgZG9zIGRhdGFmcmFtZXMgZGUgc3RvcHdvcmRzLCBlbGltaW5hbW9zIGxhcyBmaWxhcyBkdXBsaWNhZGFzIHkgZ3VhcmRhbW9zIGVsIHJlc3VsdGFkbyBlbiB1biBudWV2byBkYXRhZnJhbWUgbGxhbWFkbyAic3RvcF93b3Jkc19mdWxsIi4gRXN0ZSBkYXRhZnJhbWUgY29udGllbmUgbGEgbGlzdGEgY29tcGxldGEgeSDDum5pY2EgZGUgc3RvcHdvcmRzIG5vcm1hbGl6YWRhcy4KCmNvcnB1c190aWR5IDwtIGNvcnB1c190aWR5ICU+JSAKICBhbnRpX2pvaW4oc3RvcF93b3Jkc19mdWxsKQoKIyBFbiBlc3RlIGPDs2RpZ28gZWxpbWluYW1vcyBsYXMgc3RvcHdvcmRzIGRlbCBjb3JwdXMgZGUgdGV4dG8gImNvcnB1c190aWR5IiB1dGlsaXphbmRvIGxhIGxpc3RhIGRlIHN0b3B3b3JkcyBjb250ZW5pZGEgZW4gZWwgZGF0YWZyYW1lICJzdG9wX3dvcmRzX2Z1bGwiLiBFc3RlIHBhc28gZW4gZWwgcHJvY2VzYW1pZW50byBkZSB0ZXh0byBlcyBuZWNlc2FyaW8gcGFyYSBlbGltaW5hciBwYWxhYnJhcyBxdWUgbm8gYXBvcnRhbiBzaWduaWZpY2FkbyBwYXJhIGVsIGFuw6FsaXNpcyBwb3N0ZXJpb3IuCmBgYAoKIyMjIENvcnJlY2Npw7NuICJhw7FvcyIKCmBgYHtyIGNvcnJlY2Npb259CmNvcnB1c190aWR5IDwtIGNvcnB1c190aWR5ICU+JSAKICBtdXRhdGUod29yZCA9IGNhc2Vfd2hlbigKICAgIHdvcmQgPT0gJ2FubycgfiAnYW5pbycsCiAgICB3b3JkID09ICdhbm9zJyB+ICdhbmlvJywKICAgIFRSVUUgfiB3b3JkCiAgKSkKYGBgCgojIyBFdGFwYSAyOiBDb25zaWduYXMKCsK/Q3XDoWxlcyBzb24gbGFzIHBhbGFicmFzIG3DoXMgdXRpbGl6YWRhcyBlbiBjYWRhIHVubyBkZSBsb3MgbWVkaW9zPyDCv1B1ZWRlbiB2ZXJzZSBkaWZlcmVuY2lhcz8gKFRlbmVyIGVuIGN1ZW50YSBsYXMgZGlmZXJlbnRlcyBtw6l0cmljYXMgdHJhYmFqYWRhcyBlbiBlbCBjdXJzbzogdGYsIHRmLWlkZiwgZXRjLikgR2VuZXJhciBsYXMgdmlzdWFsaXphY2lvbmVzIHF1ZSBjb25zaWRlcmUgbcOhcyBwZXJ0aW5lbnRlcyBwYXJhIHJlc3BvbmRlciBsYSBwcmVndW50YS4KCiMjIyBFeHBsb3JhY2nDs24gcGFsYWJyYXMgbcOhcyBmcmVjdWVudGVzCgpDb21lbnphbW9zIGV4cGxvcmFuZG8gZWwgY29ycHVzIGVuIGZvcm1hdG8gdGlkeSBwYXJhIGlkZW50aWZpY2FyIHLDoXBpZGFtZW50ZSBhbGd1bm9zIGRlIGxvcyB0w6lybWlub3MgbcOhcyBmcmVjdWVudGVzIHBvciBtZWRpby4KCmBgYHtyIGV4cGxvcmVfZnJlcXN9CmNvcnB1c190aWR5ICU+JQogICAgICAgIGdyb3VwX2J5KHdvcmQsIG1lZGlvKSAlPiUKICAgICAgICBzdW1tYXJpc2Uobj1uKCkpICU+JQogICAgICAgIGFycmFuZ2UoZGVzYyhuKSkKCiMgTWVkaWFudGUgZXN0ZSBjw7NkaWdvIGFncnVwYW1vcyBsYXMgcGFsYWJyYXMgw7puaWNhcyBlbiBlbCBjb3JwdXMsIGNvbnRhbW9zIGVsIG7Dum1lcm8gZGUgb2N1cnJlbmNpYXMgZGUgY2FkYSBwYWxhYnJhIHkgbGFzIG9yZGVuYW1vcyBlbiBmdW5jacOzbiBkZSBzdSBmcmVjdWVuY2lhIGRlIGFwYXJpY2nDs24sIG1vc3RyYW5kbyBwcmltZXJvIGxhcyBwYWxhYnJhcyBtw6FzIGZyZWN1ZW50ZXMuCgpjb3JwdXNfdGlkeSAlPiUKICBmaWx0ZXIobWVkaW8gPT0gJ2luZm9iYWUnKSAgJT4lIAogIGdyb3VwX2J5KHdvcmQpICU+JQogIHN1bW1hcmlzZShuPW4oKSkgJT4lCiAgYXJyYW5nZShkZXNjKG4pKQogICAgICAgIAojIE1pcmFkYSByw6FwaWRhIGEgbGFzIHBhbGFicmFzIG3DoXMgZnJlY3VlbnRlcyBkZSBhbGd1bm9zIG1lZGlvcwoKY29ycHVzX3RpZHkgJT4lCiAgICAgICAgZ3JvdXBfYnkobWVkaW8sIHdvcmQpICU+JQogICAgICAgIHN1bW1hcmlzZShuPW4oKSkgJT4lCiAgICAgICAgYXJyYW5nZShkZXNjKG4pKSAlPiUKICAgICAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gbWVkaW8sCiAgICAgICAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBuKQoKIyBFc3RlIGPDs2RpZ28gY2FsY3VsYSBlbCByZWN1ZW50byBkZSBvY3VycmVuY2lhcyBkZSBjYWRhIHBhbGFicmEgZW4gZWwgY29ycHVzIGRlIHRleHRvIGFncnVwYWRvIHNlZ8O6biBlbCBtZWRpbyBkZSBjb211bmljYWNpw7NuLCB5IGx1ZWdvIHJlb3JnYW5pemEgZXN0b3MgcmVzdWx0YWRvcyBlbiB1biBmb3JtYXRvIG3DoXMgYW5jaG8gZG9uZGUgY2FkYSBtZWRpbyB0aWVuZSBzdXMgcmVjdWVudG9zIGRlIG9jdXJyZW5jaWFzIGFzb2NpYWRvcyBwYXJhIGNhZGEgcGFsYWJyYS4KCmBgYAoKYGBge3Igd29yZF9jb3VudHN9CndvcmRfY291bnRzXzEwIDwtIGNvcnB1c190aWR5ICU+JQogICAgICAgIGdyb3VwX2J5KG1lZGlvLCB3b3JkKSAlPiUKICAgICAgICBzdW1tYXJpc2Uobj1uKCkpICU+JQogICAgICAgIHNsaWNlX21heChuLCBuID0gMTApICU+JSAKICAgICAgICB1bmdyb3VwKCkKCiMgRW4gZXN0ZSBwYXNvIGNhbGN1bGFtb3MgZWwgcmVjdWVudG8gZGUgb2N1cnJlbmNpYXMgZGUgY2FkYSBwYWxhYnJhIGVuIGVsIGNvcnB1cywgYWdydXBhZGFzIHBvciBsYSBjb2x1bW5hICJtZWRpbyIsIHkgYWxtYWNlbmEgZXN0b3MgcmVjdWVudG9zIGVuIHVuIG51ZXZvIGRhdGFmcmFtZSBsbGFtYWRvICJ3b3JkX2NvdW50cyIgcGFyYSBwYXNhcmxlIGEgbGEgZnVuY2nDs24gZGUgZ3JhZmljYWRvLgoKd29yZF9jb3VudHNfYWxsXzEwIDwtIGNvcnB1c190aWR5ICU+JQogICAgICAgIGdyb3VwX2J5KHdvcmQpICU+JQogICAgICAgIHN1bW1hcmlzZShuPW4oKSkgJT4lCiAgICAgICAgc2xpY2VfbWF4KG4sIG4gPSAxMCkgJT4lIAogICAgICAgIHVuZ3JvdXAoKSAlPiUgCiAgICAgICAgbXV0YXRlKG1lZGlvID0gJ2dlbmVyYWwnKQoKIyBBcXXDrSBjYWxjdWxhbW9zIGVsIHJlY3VlbnRvIGRlIG9jdXJyZW5jaWFzIGRlIGNhZGEgcGFsYWJyYSBlbiBlbCBjb3JwdXMsIHNpbiBhZ3J1cGFyIHBvciBtZWRpby4gTHVlZ28sIHNlbGVjY2lvbmFtb3MgbGFzIDEwIHBhbGFicmFzIG3DoXMgY29tdW5lcyBlbiB0b2RvIGVsIGNvcnB1cy4gQSBlc3RvcyByZXN1bHRhZG9zIGxlcyBhZ3JlZ2Ftb3MgbGEgZXRpcXVldGEgImdlbmVyYWwiIGVuIGxhIGNvbHVtbmEgIm1lZGlvIiBwYXJhIHBvZGVyIGNvbXBhcmFyIHZpc3VhbG1lbnRlIGxhIGRpc3RyaWJ1Y2nDs24gZ2VuZXJhbCBjb24gbGEgZGlzdHJpYnVjacOzbiBwb3IgbWVkaW8KCndvcmRfY291bnRzX3RvcF8xMCA8LSB3b3JkX2NvdW50c18xMCAlPiUgCiAgYmluZF9yb3dzKHdvcmRfY291bnRzX2FsbF8xMCkKCiMgVW5pZmljbyBlbiB1biBkYXRhc2V0CmBgYAoKQSBjb250aW51YWNpw7NuIGNyZWFtb3MgdW5hIGZ1bmNpw7NuIHBhcmEgYXV0b21hdGl6YXIgdG9kYXMgbGFzIHZpc3VhbGl6YWNpb25lcyByZWxhY2lvbmFkYXMgYSBsYXMgbcOpdHJpY2FzIGRlbCBjb3JwdXMuIAoKYGBge3IgY3JlYXJfdml6fQpjcmVhcl9ncmFmX3dvcmRzIDwtIGZ1bmN0aW9uKGRhdGEpIHsKICBtZWRpb3MgPC0gdW5pcXVlKGRhdGEkbWVkaW8pCiAgZ3JhZmljb3MgPC0gbGlzdCgpCiAgCiAgZm9yIChtZWRpb19hY3R1YWwgaW4gbWVkaW9zKSB7CiAgICBkYXRvc19tZWRpbyA8LSBkYXRhICU+JQogICAgICBmaWx0ZXIobWVkaW8gPT0gbWVkaW9fYWN0dWFsKSAlPiUKICAgICAgbXV0YXRlKHdvcmQgPSBmY3RfcmVvcmRlcih3b3JkLCBuKSkKICAgIAogICAgZ3JhZmljbyA8LSBnZ3Bsb3QoZGF0b3NfbWVkaW8sIGFlcyhuLCB3b3JkKSkgKwogICAgICAgICAgICAgICAgZ2VvbV9jb2woKSArCiAgICAgICAgICAgICAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbiksIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpLCBmYW1pbHkgPSAiQ291cmllciIsIGNvbG9yID0gIndoaXRlIikgKwogICAgICAgICAgICAgICAgbGFicyh0aXRsZSA9IG1lZGlvX2FjdHVhbCwKICAgICAgICAgICAgICAgICAgICAgeCA9ICJGcmVjdWVuY2lhIiwKICAgICAgICAgICAgICAgICAgICAgeSA9ICJQYWxhYnJhIikgKwogICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKwogICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJDb3VyaWVyIikpCiAgICAKICAgIGdyYWZpY29zW1ttZWRpb19hY3R1YWxdXSA8LSBncmFmaWNvCiAgfQogIAogIHdyYXBfcGxvdHMoZ3JhZmljb3MpCn0KCiMgRXN0YSBmdW5jacOzbiBjcmVhIHBhcmEgY2FkYSBtZWRpbyB1biBncsOhZmljbyBjb24gZWwgdG9wIDEwIGRlIHBhbGFicmFzIHNlZ8O6biBjYWRhIG3DqXRyaWNhIGFuYWxpemFkYQoKYGBgCgpFbCBlamUgeCBtdWVzdHJhIGxhcyBwYWxhYnJhcyB5IGVsIGVqZSB5IG11ZXN0cmEgbGEgZnJlY3VlbmNpYSBkZSBjYWRhIHBhbGFicmEuIEVsIGdyw6FmaWNvIGVzdMOhIHNlZ21lbnRhZG8gZW4gcGFuZWxlcyBwb3IgY2FkYSBtZWRpbywgeSBsYXMgZXNjYWxhcyBkZWwgZWplIHkgc2UgYWp1c3RhbiBwYXJhIGNhZGEgcGFuZWwgaW5kaXZpZHVhbC4KCmBgYHtyIHZpen0KY3JlYXJfZ3JhZl93b3Jkcyh3b3JkX2NvdW50c190b3BfMTApCgojIEVzdGUgY8OzZGlnbyBnZW5lcmEgdW5hIHZpc3VhbGl6YWNpw7NuIGRlIGxhcyBwYWxhYnJhcyBtw6FzIGNvbXVuZXMgZW4gZWwgY29ycHVzLCBkZXNnbG9zYWRhcyBwb3IgbWVkaW8geSBlbiBnZW5lcmFsLiBMYXMgcGFsYWJyYXMgbcOhcyBjb211bmVzIHNlIG11ZXN0cmFuIGVuIG9yZGVuIGRlc2NlbmRlbnRlIGRlIGZyZWN1ZW5jaWEgZW4gY2FkYSBjYXRlZ29yw61hLgpgYGAKCkFsZ3VuYXMgb2JzZXJ2YWNpb25lcyBwcmVsaW1pbmFyZXM6CgotICAgQ2xhcsOtbiBlIEluZm9iYWUgYXBvcnRhbiBjYWRhIHVubyBlbCAyMiUgZGUgbGFzIG5vdGljaWFzIGFsIGNvcnB1cy4gRWwgcGVzbyBkZSBsYXMgcGFsYWJyYXMgbcOhcyBpbXBvcnRhbnRlcyBwYXJhIGVzdG9zIG1lZGlvcyBlbiBlbCByYW5raW5nIGdlbmVyYWwgZXMgYWx0by4KCi0gICBBw7FvIHBhcmVjZSB1bmEgc3RvcHdvcmQgcG9ycXVlIGVzIHVuYSBwYWxhYnJhIHF1ZSBwcmVzZW50YSB1bmEgZnJlY3VlbmNpYSBhbHRhIGludHJhIGUgaW50ZXIgbWVkaW9zLgoKLSAgIENyw7NuaWNhLCBMTiBlIEluZm9iYWUgc29uIGxvcyDDum5pY29zIHF1ZSBubyBpbmNsdXllbiBub21icmVzIHByb3Bpb3MKZW50cmUgbGFzIHBhbGFicmFzIG3DoXMgZnJlY3VlbnRlcy4KCi0gICBFbiBMTiBoYXkgbXVjaGFzIHJlZmVyZW5jaWFzIGEgcmVkZXMgc29jaWFsZXMgeSBhY2Npb25lcyBlbiBlbGxhcyAoZ3VhcmRhciwgY29tcGFydGlyKS4gTGFzIGFjY2lvbmVzIHkgImZ1ZW50ZSIgcG9kcsOtYW4gc2VyIHN0b3B3b3Jkcy4gSW5jbHVzbyBsYXMgbWVuY2lvbmVzIGEgUlJTUyBwb2Ryw61hbiBzZXIgcHJvZHVjdG8gZGVsIHNjcmFwcGluZyB5IG5vIGRlbCBjb250ZW5pZG8gZGUgbGFzIG5vdGFzLgoKLSAgIE1hY3JpIGFwYXJlY2UgY29tbyBlbCBwb2zDrXRpY28gbcOhcyBub21icmFkby4gTMOzZ2ljYW1lbnRlLCBlcyB1biByZXN1bHRhZG8gZXNwZXJhYmxlIHNpIHNlIHRpZW5lIGVuIGN1ZW50YSBxdWUgbGFzIG5vdGFzIHJlbGV2YWRhcyBjb3JyZXNwb25kZW4gYWwgw7psdGltbyBhw7FvIGRlIHN1IGdvYmllcm5vLiAKCi0gIEluZm9iYWUgeSBzb2JyZSB0b2RvIENyw7NuaWNhIHBhcmVjZW4gY3VicmlyIHRlbWFzIG3DoXMgZ2VuZXJhbGVzIHF1ZSBsb3MgZGVtw6FzIG1lZGlvcywgYWwgaW5jbHVpciBwYWxhYnJhcyBjb21vICJwb2zDrWPDrWEiLCAiY2l1ZGFkIiwgIm11amVyIiwgImhvbWJyZSIsICJjYXNhIiwgInBlcnNvbmFzIiwgIm11bmRvIiwgInZpZGEiLiBJbmNsdXNvLCBsYSBmcmVjdWVuY2lhIGRlIGxhIHBhbGFicmEgInBvbGljw61hIiBlbiBDcsOzbmljYSAoIzIgbyAjMSBzaSBzZSBleGNsdXllIGVsIHTDqXJtaW5vICJhbmlvIikgcG9kcsOtYSBpbmRpY2FyIHF1ZSBsYSBtYXlvciBwYXJ0ZSBkZSBsYSBjb2JlcnR1cmEgZGUgZXN0ZSBtZWRpbyBzZSBvcmllbnRhIGEgbGEgdGVtw6F0aWNhIHBvbGljaWFsLiBEZSB0b2RhcyBmb3JtYXMsIGVuIGVsIHRvcCAxMCB0YW1iacOpbiBhcGFyZWNlbiBwYWxhYnJhcyBjb21vICJnb2JpZXJubyIgeSAicHJlc2lkZW50ZSIuCgotICAgTGEgcGFsYWJyYSAiZnJlbnRlIiBhcGFyZWNlIGNvbiBmcmVjdWVuY2lhIGVuIHZhcmlvcyBtZWRpb3MsIGxvIHF1ZSBub3MgaW5jbGluYSBhIGNvcnJvYm9yYXIgc2kgcmVmaWVyZSBhIGZyZW50ZXMgZWxlY3RvcmFsZXMgbyBzaW1wbGVtZW50ZSBhIHVuIGFkdmVyYmlvIGRlIGx1Z2FyIHksIGVuIGVzdGUgY2FzbywgZGViZXLDrWEgY29uc2lkZXJhcnNlIGNvbW8gc3RvcHdvcmQuCgotICAgQSBtb2RvIGRlIHPDrW50ZXNpcywgcG9kcsOtYSBhZHZlcnRpcnNlIHF1ZSBsYXMgcGFsYWJyYXMgY29uIG1heW9yIGZyZWN1ZW5jaWEgZGUgYXBhcmljacOzbiBlbiBsYSB0b3RhbGlkYWQgZGUgbG9zIG1lZGlvcyBzZSBjb3JyZXNwb25kZW4gY29uIGxhIHRlbcOhdGljYSBwb2zDrXRpY2EuIEVzIHByb2JhYmxlIHF1ZSBlc3RhIHZpbmN1bGFjacOzbiBzZWEgdW4gZGVzcHJlbmRpbWllbnRvIGRlbCBlc2NlbmFyaW8gZWxlY3RvcmFsIHF1ZSBzaWduw7MgZWwgcGVyw61vZG8gYmFqbyBhbsOhbGlzaXMuCgojIyMgVGVybSBGcmVxdWVuY3kgLSBURjoKCkEgbW9kbyBkZSBwcsOhY3RpY2EgZ2VuZXJhbW9zIGVsIGPDoWxjdWxvIGRlIHRlcm0gZnJlcXVlbmN5IG1hbnVhbG1lbnRlIHBhcmEgdmlzdWFsaXphciBsYSBkaXN0cmlidWNpw7NuIGRlIHTDqXJtaW5vcyBzZWfDum4gc3UgZnJlY3VlbmNpYSByZXNwZWN0byBhbCB0b3RhbCBkZSB0w6lybWlub3MsIGFwZXJ0dXJhZG8gcG9yIG1lZGlvLgoKYGBge3Igd29yZHNfdGlkeX0KIyBMYSAidGVybSBmcmVxdWVuY3kiIChmcmVjdWVuY2lhIGRlIHTDqXJtaW5vKSBlcyB1bmEgbcOpdHJpY2EgcGFyYSBldmFsdWFyIGxhIGltcG9ydGFuY2lhIGRlIHVuYSBwYWxhYnJhIGVuIHVuIGRvY3VtZW50byBvIGNvcnB1cyBkZSBkb2N1bWVudG9zLCBhIHBhcnRpciBkZSBsYSBtZWRpY2nDs24gZGUgbGEgZnJlY3VlbmNpYSBkZSBzdSBhcGFyaWNpw7NuIGVuIGNvbXBhcmFjacOzbiBjb24gZWwgbsO6bWVybyB0b3RhbCBkZSBwYWxhYnJhcy4KCndvcmRzX3RpZHkgPC0gY29ycHVzX3RpZHkgJT4lIAogIGdyb3VwX2J5KG1lZGlvLCB3b3JkKSAlPiUgIyBlbiBmdW5jacOzbiBkZSBsYSBjb25zaWduYSwgaGFjZW1vcyBlbCBjb250ZW8gZGUgdMOpcm1pbm9zIHBvciBtZWRpbwogIHN1bW1hcmlzZShuPW4oKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkgCgp0b3RhbF93b3JkcyA8LSB3b3Jkc190aWR5ICU+JQogICAgICAgIGdyb3VwX2J5KG1lZGlvKSAlPiUKICAgICAgICBzdW1tYXJpemUodG90YWwgPSBzdW0obikpIAoKd29yZHNfdGlkeSA8LSB3b3Jkc190aWR5ICU+JSAKICBsZWZ0X2pvaW4odG90YWxfd29yZHMpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkKCiMgRW4gZXN0ZSBjw7NkaWdvIGNvbnRhbW9zIGxhIGZyZWN1ZW5jaWEgZGUgY2FkYSBwYWxhYnJhIGVuIGVsIGNvcnB1cyBkZWwgbWVkaW8geSB0YW1iacOpbiBlbCB0b3RhbCBkZSBwYWxhYnJhcyBlbiBlbCBtZWRpby4gQ29uIGVzdG9zIGRhdG9zIHBvZGVtb3MgY2FsY3VsYXIgYSBjb250aW51YWNpw7NuIGxhIGltcG9ydGFuY2lhIGRlIGNhZGEgdMOpcm1pbm8gZW4gZWwgbWVkaW8uCmBgYAoKIyMgUFJVRUJJVEFTU1MKYGBge3J9CndvcmRzX3RpZHlfdGVzdCA8LSBjb3JwdXNfdGlkeSAlPiUgCiAgZ3JvdXBfYnkoaWQsIHdvcmQsIG1lZGlvKSAlPiUgCiAgc3VtbWFyaXNlKG49bigpKSAlPiUgCiAgYXJyYW5nZShkZXNjKG4pKSAKCnRvdGFsX3dvcmRzX3Rlc3QgPC0gd29yZHNfdGlkeV90ZXN0ICU+JQogICAgICAgIGdyb3VwX2J5KGlkKSAlPiUKICAgICAgICBzdW1tYXJpemUodG90YWwgPSBzdW0obikpIAoKd29yZHNfdGlkeV90ZXN0IDwtIHdvcmRzX3RpZHlfdGVzdCAlPiUgCiAgbGVmdF9qb2luKHRvdGFsX3dvcmRzX3Rlc3QpICU+JSAKICB1bmdyb3VwKCkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkKCnQgPC0gd29yZHNfdGlkeV90ZXN0ICU+JSBtdXRhdGUodGYgPSBuL3RvdGFsKSAlPiUKICAgICAgICAgIGdncGxvdChhZXModGYpKSArCiAgICAgICAgICAgICAgICBnZW9tX2hpc3RvZ3JhbShzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgICAgICAgICAgICAgICBjb29yZF9mbGlwKCkrCiAgICAgICAgICAgICAgICAjeGxpbShOQSwgMC4wMDAyKSArCiAgICAgICAgICAgICAgICBmYWNldF93cmFwKH5tZWRpbykgKwogICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKwogICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJDb3VyaWVyIikpCiAgICAgICAgICAgICAgICAgIAoKYGBgCkVuIGVzdGEgdmlzdWFsaXphY2nDs24gcG9kZW1vcyBpbnRlcmFjdHVhciBwYXJhIGlkZW50aWZpY2FyIGxhIGNhbnRpZGFkIGRlIHTDqXJtaW5vcyBwb3IgdGY6CgpgYGB7ciB0Zl92aXpfbWFudWFsfQp0Zl92aXogPC0gd29yZHNfdGlkeSAlPiUgbXV0YXRlKHRmID0gbi90b3RhbCkgJT4lCiAgICAgICAgICBnZ3Bsb3QoYWVzKHRmKSkgKwogICAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgICAgICAgICAgICAgY29vcmRfZmxpcCgpKwogICAgICAgICAgICAgICAgeGxpbShOQSwgMC4wMDAyKSArCiAgICAgICAgICAgICAgICBmYWNldF93cmFwKH5tZWRpbykgKwogICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKwogICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJDb3VyaWVyIikpCiAgICAgICAgICAgICAgICAgIApnZ3Bsb3RseSh0Zl92aXopCgpgYGAKClZlbW9zIHF1ZSBwYXJhIGxvcyBtZWRpb3MgbcOhcyBtYXNpdm9zIHNlIGN1bXBsZSBsYSBMZXkgZGUgWmlwZjogcG9jYXMgcGFsYWJyYXMgb2N1cnJlbiBtdWNoYXMgdmVjZXMuIExvcyBjb3JwdXMgZGUgQ3LDs25pY2EsIE1pbnV0byBVbm8geSBUw6lsYW0gdGllbmVuIG1lbm9zIHTDqXJtaW5vcyBxdWUgbG9zIGRlbcOhcyB5IGxhIHRmIHRpZW5lIHVuIHJhbmdvIG1lbm9yICh1c28gZGUgcGFsYWJyYXMgbWVqb3IgZGlzdHJpYnVpZG8pLgoKIyMjIFRlcm0gRnJlcXVlbmN5IC0gSW52ZXJzZSBEb2N1bWVudCBGcmVxdWVuY3kgKFRGLUlERikKCkVuIGVzdGEgZXRhcGEgY29tcGxlbWVudGFtb3MgZWwgYW7DoWxpc2lzIGNvbiBsYXMgbcOpdHJpY2FzIGRlIGlkZiB5IHRmX2lkZiwgcGFyYSBvYnRlbmVyIHVuYSBtZWRpZGEgbcOhcyBjb21wbGV0YSBkZSBsYSBpbXBvcnRhbmNpYSBkZSBsb3MgdMOpcm1pbm9zIGVuIGVsIGNvcnB1cyBkZSBkb2N1bWVudG9zLgoKYGBge3IgdGZfaWRmfQp0Zl9pZGYgPC0gd29yZHNfdGlkeSAlPiUgCiAgYmluZF90Zl9pZGYod29yZCwgbWVkaW8sIG4pIAoKdGZfaWRmICU+JSBzZWxlY3QoLXRvdGFsKSAlPiUgCiAgYXJyYW5nZShkZXNjKHRmX2lkZikpCgojIEVuIGVzdGUgY8OzZGlnbyBjYWxjdWxhbW9zIGxhcyBtw6l0cmljYXMgdGYsIGlkZiB5IHRmX2lkZiBwYXJhIHRvZG9zIGxvcyB0w6lybWlub3MgZGVsIGNvcnB1cwpgYGAKCkNyZWFtb3MgdHJlcyBkYXRhc2V0LCB1bm8gcG9yIG3DqXRyaWNhLCBjb24gZWwgdG9wIHRlbiBkZSB0w6lybWlub3MgcGFyYSBwYXNhcmxlIGEgbGEgZnVuY2nDs24gZGUgdmlzdWFsaXphY2nDs24uCgpgYGB7ciB0b3BfdGVuX21ldHJpY2FzfQp0Zl8xMCA8LSB0Zl9pZGYgJT4lCiAgICAgICAgZ3JvdXBfYnkobWVkaW8pICU+JQogICAgICAgIHNsaWNlX21heCh0ZiwgbiA9IDEwKSAlPiUgCiAgICAgICAgc2VsZWN0KG1lZGlvLCB3b3JkLCB0ZikgJT4lIAogICAgICAgIHJlbmFtZShuID0gdGYpICU+JQogICAgICAgIG11dGF0ZShuID0gbG9nKG4gKyAxKSAqIDEwMCkgJT4lIAogICAgICAgIG11dGF0ZShuID0gcm91bmQobiwgNCkpCgppZGZfMTAgPC0gdGZfaWRmICU+JQogICAgICAgICAgZ3JvdXBfYnkobWVkaW8pICU+JQogICAgICAgICAgYXJyYW5nZShkZXNjKGlkZikpICU+JQogICAgICAgICAgc2xpY2VfaGVhZChuID0gMTApICU+JQogICAgICAgICAgc2VsZWN0KG1lZGlvLCB3b3JkLCBpZGYpICU+JQogICAgICAgICAgcmVuYW1lKG4gPSBpZGYpICU+JQogICAgICAgICAgbXV0YXRlKG4gPSBsb2cobiArIDEpICogMTAwKSAlPiUgCiAgICAgICAgICBtdXRhdGUobiA9IHJvdW5kKG4sIDQpKQoKdGZfaWRmXzEwIDwtIHRmX2lkZiAlPiUKICAgICAgICBncm91cF9ieShtZWRpbykgJT4lCiAgICAgICAgYXJyYW5nZShkZXNjKGlkZikpICU+JQogICAgICAgIHNsaWNlX2hlYWQobiA9IDEwKSAlPiUgCiAgICAgICAgc2VsZWN0KG1lZGlvLCB3b3JkLCB0Zl9pZGYpICU+JSAKICAgICAgICByZW5hbWUobiA9IHRmX2lkZikgJT4lCiAgICAgICAgbXV0YXRlKG4gPSBsb2cobiArIDEpICogMTAwKSAlPiUgCiAgICAgICAgbXV0YXRlKG4gPSByb3VuZChuLCA0KSkKYGBgCgpQYXNhbW9zIGNhZGEgdW5vIGRlIGxvcyB0b3AgdGVuIGRlIHTDqXJtaW5vcyBwb3IgbGEgZnVuY2nDs24gZGUgZ3JhZmljYWRvIHBhcmEgdmlzdWFsaXphciBsb3MgdMOpcm1pbm9zIG3DoXMgaW1wb3J0YW50ZXMgc2Vnw7puIGNhZGEgbcOpdHJpY2EuCgpgYGB7ciB0Zl92aXp9CmNyZWFyX2dyYWZfd29yZHModGZfMTApCmBgYAoKYGBge3IgaWRmX3Zpen0KY3JlYXJfZ3JhZl93b3JkcyhpZGZfMTApCmBgYAoKYGBge3IgdGZfaWRmX3Zpen0KY3JlYXJfZ3JhZl93b3Jkcyh0Zl9pZGZfMTApCmBgYAoKQW7DoWxpc2lzIHByZWxpbWluYXIgZGUgVEYtSURGOgoKLSAgIEVuIHByaW1lciBsdWdhciBpZGVudGlmaWNhbW9zIGxhIG5lY2VzaWRhZCBkZSBhZ3JlZ2FyIHBhbGFicmFzIGFsCiAgICBsaXN0YWRvIGRlIHN0b3B3b3Jkcy4gRW50ZW5kZW1vcyBxdWUgbm8gYXBvcnRhbiBpbmZvcm1hY2nDs24gc29icmUgZWwKICAgIGNvbnRlbmlkbyB1IG9yaWVudGFjacOzbiBkZSBsYXMgbm90aWNpYXMgeSBhIHN1IHZleiBzb24gw7puaWNvcyBwYXJhCiAgICBhbGd1bm9zIG1lZGlvcyB5IGVzbyBnZW5lcmEgdW4gYWx0byB0Zl9pZGYuCgogICAgRWplbXBsb3M6IGpwZSwgYXAsIGVtaiwgY3JvbmljYS5jb20uYXIsIGZ2YXpxdWV6LCBjcm9uaWNhdmlyYWxlcywKICAgIGhkLCBwZW1leCwgZ3QsIGFmdiwganBnLCBtaW51dG91bm8uY29tLCBhbWJpdG8uY29tLCBpdmFub3ZpY2gsCiAgICBsb2FkaW5nLCBwYWdpbmFpLCBwcm90ZWN0ZWQsIGx4cywgZW1haWwsIHIuYyxjcCwgZmVsLGwubCxkLnMsIGVhLAogICAgZi5mLCBmLmQucywgZmgsIGEuZywgcGN0LCB0ZWxhbS5sYSwgbmFjaW9uYWwuZWwKCi0gICBQb2RlbW9zIGlkZW50aWZpY2FyICJmaXJtYXMiIGRlIHBlcmlvZGlzdGFzIChub21icmVzIGRlIHVzdWFyaW8gbwogICAgc2lnbGFzKSBxdWUgdGFtcG9jbyBzb24gaW5mb3JtYXRpdmFzIHNvYnJlIGVsIGNvbnRlbmlkbyBkZWwgY29ycHVzLgoKIyMgRXRhcGEgMS4yOiBBbXBsaWFjacOzbiBzdG9wd29yZHMgeSByZXByb2Nlc2FtaWVudG8gZGUgbGEgYmFzZQoKRWwgYW7DoWxpc2lzIHJlYWxpemFkbyBub3MgcGVybWl0acOzIGVuY29udHJhciBudW1lcm9zYXMgcGFsYWJyYXMgaW5jbHVpZGFzIGVuIGVsIGNvcnB1cyBxdWUgY3JlZW1vcyBzb24gcHJvZHVjdG8gZGVsIHNjcmFwcGluZy4gUHJldmlvIGEgbGEgbW9kZWxpemFjacOzbiBwYXJhIGlkZW50aWZpY2FyIHTDs3BpY29zIGNyZWVtb3MgcGVydGluZW50ZSBhbXBsaWFyIGVsIGxpc3RhZG8gZGUgc3RvcHdvcmRzIHkgcmVwZXRpciBlbCBwcmVwcm9jZXNhbWllbnRvIGRlIGxvcyBkYXRvcy4gCgpQYXJhIGFtcGxpYXIgZWwgbGlzdGFkbyBkZSBzdG9wd29yZHMgcGFydGltb3MgZGVsIGxpc3RhZG8gZGUgcGFsYWJyYXMgY29uIHN1cyBtw6l0cmljYXMgZGUgdGYgZSBpZGYuCgpgYGB7ciBzdG9wd29yZHNfdjJ9CiMgTWVkaWFudGUgZWwgc2lndWllbnRlIGPDs2RpZ28gZWxpbWluYW1vcyBwYWxhYnJhcyBxdWUgaW5jbHV5ZW4gcHVudG9zIHkgcGFyZWNlbiBwcm9kdWN0byBkZSBzY3JhcHBpbmcgd2ViLiAKbnVldmFzX3N0b3B3b3Jkc18xIDwtIHRmX2lkZiAlPiUgCiAgc2VsZWN0KHdvcmQpICU+JSAKICBmaWx0ZXIoZ3JlcGwoJ1xcLicsIHdvcmQpKSAlPiUgCiAgcHVsbCh3b3JkKQoKIyBFbiBlc3RlIHBhc28gZWxpbWluYW1vcyBwYWxhYnJhcyBkZSBkb3MgY2FyYWN0ZXJlcywgc2Fsdm8gY29udGFkYXMgZXhjZXBjaW9uZXMgcXVlIHRpZW5lbiBzZW50aWRvLgpudWV2YXNfc3RvcHdvcmRzXzIgPC0gdGZfaWRmICU+JSAKICBzZWxlY3Qod29yZCkgJT4lIAogIGZpbHRlcihzdHJfbGVuZ3RoKHdvcmQpID09IDIgJiAhKHdvcmQgJWluJSBjKCdwaicsICdmZScsICdkdCcsICd0dicsICdrbScsICdjdicsICdkcicsICdpdCcsICdkaicsICd1eCcpKSkgJT4lIAogIHB1bGwod29yZCkKCiMgRXN0ZSBjw7NkaWdvIGVsaW1pbmEgcGFsYWJyYXMgZGUgdHJlcyBjYXJhY3RlcmVzLiBQYXJlY2UgaGFiZXIgbcOhcyBzaWdsYXMgeSBwYWxhYnJhcyBjb3J0YXMgcXVlIHRpZW5lbiBzZW50aWRvIHBlcm8sIGRhZGEgbGEgZ3JhbiBjYW50aWRhZCBkZSBzdG9wd29yZHMgZGUgMyBjYXJhY3RlcmVzLCBoYXkgbW90aXZvcyBwYXJhIGluY2x1aXIgZXN0ZSBwYXNvLgpudWV2YXNfc3RvcHdvcmRzXzMgPC0gdGZfaWRmICU+JSAKICBzZWxlY3Qod29yZCkgJT4lIAogIGZpbHRlcihzdHJfbGVuZ3RoKHdvcmQpID09IDMgJiAhKHdvcmQgJWluJSBjKCdzYW4nLCAnbWlsJywgJ2xleScsICdhZnAnLCAnc3VyJywgJ2ZtaScsICd1c2QnLCAncmlvJywgJ2dvbCcsICdwYXonLCAnbWFyJywgJ29ybycsICdyZWQnLCAnbHV6JywgJ3ZveicgLCAncm9sJywgJ3NvbCcsICdnYXMnLCAncGllJywgJ3BhcicsICdwcm8nLCAndmlhJywgJ29udScsICd5cGYnLCAnaXZhJywgJ2FmYScsICdwYmknLCAnYmFyJywgJ2NmaycsICdlamUnLCAncmV5JywgJ2F0cCcsICdkb24nLCAnZmJpJywgJ2dheScsICdwc2cnLCAndWJhJywgJ3VjcicsICdjZW8nLCAnb25nJywgJ2ZlZCcsICdvam8nLCAnZGVhJywgJ3V2YScsICdjZ3QnLCAndWZpJywgJ2FwcCcsICdnaWwnLCAndmloJywgJ25iYScsICdiYmMnLCAnZXZvJywgJ2hpcCcsICdob3AnLCAnZm94JywgJ25iYycsICdyYXAnLCAnYWRuJywgJ2FsYScsICdldmEnLCAncGFuJywgJ3plbicsICdhZnYnLCAnY2NrJywgJ2VjbycsICdvY2EnLCAndGlvJywgJ2NubicsICdjaWEnLCAiZG5pIiwgInVpYSIsICJmZHQiLCAidWlmIiwgImhibyIsICJtbHMiLCAib2VhIiwgIm1lcCIsICJnbmMiLCAiYXVoIiwgImNoZSIsICJvaWwiLCAiZ2VuIiwgImFnbiIsCSJmcHYiLCAibGFtIiwJInBpYiIsICJjbmUiLCJkdW8iLCAidm94IiwgImRvdyIsICJ1Y2EiLCAiZG51IiwgInBleiIsICJwZmEiLCAicGR0IiwgImZkYSIsICJmY2kiLCAib21zIiwgInBzYSIsICJmZW8iLCAiY3RhIiwgImVnbyIsCQkJCQoiZmFhIiwJInV0ZSIsICJhdGUiLAkibGlvIiwgImZwdCIsICJzdXYiLCAiZ2JhIiwJIml6cSIsICJhcm8iLCAic21uIiwgInRudCIsCSJ1Y28iLCAiaXBjIiwgInNhYSIsICJ0bXoiLCAiY2NsIiwgImdlbCIsICJ2aXAiLCAiZXNpIiwJInJlcyIsICJrdW4iLCAidHNqIiwgImFmaSIsCSJwdHMiLCAiY25oIiwgImFqbyIsICJhY3YiLCAiYm13IiwgImJ1cyIsICJncHMiLCAiaWxlIiwJImlvcyIsICJ1bmMiLAkiem9vIiwgImp1cCIsICJ0b3MiLCAidW5sIiwJInVwbCIsICJ6ZW8iLCAiYXZlIiwgIm10ZSIsICJtcG4iLCAiYXBuIiwgIm1hbyIsICJwYmEiLCAic21zIiwgImNudiIsICJtZHoiLCAiZm9sIiwgImlzbyIpKSkgJT4lIAogIHB1bGwod29yZCkKCm51ZXZhc19zdG9wd29yZHMgPC0gZGF0YS5mcmFtZSh3b3JkID0gbnVldmFzX3N0b3B3b3Jkc18xKSAlPiUgCiAgYmluZF9yb3dzKGRhdGEuZnJhbWUod29yZCA9IG51ZXZhc19zdG9wd29yZHNfMikpICU+JSAKICBiaW5kX3Jvd3MoZGF0YS5mcmFtZSh3b3JkID0gbnVldmFzX3N0b3B3b3Jkc18zKSkKCnN0b3Bfd29yZHNfZnVsbF92MiA8LSBzdG9wX3dvcmRzX2Z1bGwgJT4lIAogIGJpbmRfcm93cyh0aWJibGUod29yZCA9IGMoJ2VtYmVkJywgJ2FuaW8nLCAnYW5vJywgJ2Fub3MnLCAnZ3VzdGEnLCAndHdpdHRlcicsICdmYWNlYm9vaycsICdjb21lbnRhcicsICdmdWVudGUnLCAnd2hhdHNhcHAnLCAnZ3VhcmRhcicsICdjb21wYXJ0aXInLCAnbWFpbCcsICdsb2FkaW5nJywgJ2VtYWlsJywgJ3BhZ2luYWknLCAnZWx0cmVjZScsICdpbmZvYmFlJywgJ2h0dHAnLCAnaHR0cHMnLCAnYXR0cmlidXRlJywgJ2ZpbmRfYWxsJywgJ25vbmV0eXBlJywgJ29iamVjdCcsICdyZWFkJyApKSkgJT4lIAogIGJpbmRfcm93cyhudWV2YXNfc3RvcHdvcmRzKQpgYGAKCkEgcGFydGlyIGRlIGxhIG51ZXZhIGxpc3RhIGRlIHN0b3B3b3JkcywgcmVjcmVhbW9zIGVsIGNvcnB1cyBlbiBmb3JtYXRvIHRpZHkgeSB0YW1iacOpbiBlbCBkYXRhZnJhbWUgZW4gZm9ybWF0byBtZWRpby90w6lybWluby9uIHBhcmEgY29udGludWFyIGNvbiBlbCBhbsOhbGlzaXMgZGUgbcOpdHJpY2FzLgoKYGBge3IgY29ycHVzX3RpZHlfdjJ9CmNvcnB1c190aWR5X3YyIDwtIGNvcnB1c190aWR5ICU+JSAKICBhbnRpX2pvaW4oc3RvcF93b3Jkc19mdWxsX3YyKQpgYGAKCmBgYHtyIHdvcmRzX3RpZHlfdjJ9IAp3b3Jkc190aWR5X3YyIDwtIGNvcnB1c190aWR5X3YyICU+JSAKICBncm91cF9ieShtZWRpbywgd29yZCkgJT4lCiAgc3VtbWFyaXNlKG49bigpKQoKdG90YWxfd29yZHNfdjIgPC0gd29yZHNfdGlkeV92MiAlPiUKICAgICAgICBncm91cF9ieShtZWRpbykgJT4lCiAgICAgICAgc3VtbWFyaXplKHRvdGFsID0gc3VtKG4pKSAKCndvcmRzX3RpZHlfdjIgPC0gd29yZHNfdGlkeV92MiAlPiUgCiAgbGVmdF9qb2luKHRvdGFsX3dvcmRzX3YyKSAlPiUgCiAgdW5ncm91cCgpICU+JSAKICBhcnJhbmdlKGRlc2MobikpCmBgYAoKIyMjIFJldmlzacOzbiBkZSBtw6l0cmljYXMKCkNyZWFtb3MgZWwgZ3LDoWZpY28gY29uIGxhIGRpc3RyaWJ1Y2nDs24gZGUgdMOpcm1pbm9zIHNlZ8O6biBzdSB0ZiBwYXJhIGlkZW50aWZpY2FyIHZhcmlhY2lvbmVzIHJlc3BlY3RvIGEgbGEgdjEuCgpgYGB7ciB0Zl92aXpfbWFudWFsX3YyfQp0Zl92aXpfdjIgPC0gd29yZHNfdGlkeV92MiAlPiUgbXV0YXRlKHRmID0gbi90b3RhbCkgJT4lCiAgICAgICAgICBnZ3Bsb3QoYWVzKHRmKSkgKwogICAgICAgICAgICAgICAgZ2VvbV9oaXN0b2dyYW0oc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogICAgICAgICAgICAgICAgY29vcmRfZmxpcCgpKwogICAgICAgICAgICAgICAgeGxpbShOQSwgMC4wMDAyKSArCiAgICAgICAgICAgICAgICBmYWNldF93cmFwKH5tZWRpbykgKwogICAgICAgICAgICAgICAgdGhlbWVfY2xhc3NpYygpKwogICAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0gMC41KSwKICAgICAgICAgICAgICAgICAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICBheGlzLnRpY2tzLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICAgICAgICAgICAgICB0ZXh0ID0gZWxlbWVudF90ZXh0KGZhbWlseSA9ICJDb3VyaWVyIikpCiAgICAgICAgICAgICAgICAgIApnZ3Bsb3RseSh0Zl92aXpfdjIpCmBgYAoKCmBgYHtyIHRmX2lkZl92Mn0KdGZfaWRmX3YyIDwtIHdvcmRzX3RpZHlfdjIgJT4lIAogIGJpbmRfdGZfaWRmKHdvcmQsIG1lZGlvLCBuKSAKYGBgCgpDcmVhbW9zIGxvcyBkYXRhc2V0IG5lY2VzYXJpb3MgcGFyYSB2aXN1YWxpemFyIGxvcyBwcmluY2lwYWxlcyB0w6lybWlub3MgcG9yIG3DqXRyaWNhIHkgcG9yIG1lZGlvLCBidXNjYW5kbyBkaWZlcmVuY2lhcyByZXNwZWN0byBhIGxhIHYxLgoKYGBge3IgdG9wX3Rlbl9tZXRyaWNhc192Mn0KdGZfMTBfdjIgPC0gdGZfaWRmX3YyICU+JQogICAgICAgIGdyb3VwX2J5KG1lZGlvKSAlPiUKICAgICAgICBhcnJhbmdlKGRlc2ModGYpKSAlPiUKICAgICAgICBzbGljZV9oZWFkKG4gPSAxMCkgJT4lCiAgICAgICAgc2VsZWN0KG1lZGlvLCB3b3JkLCB0ZikgJT4lIAogICAgICAgIHJlbmFtZShuID0gdGYpICU+JQogICAgICAgIG11dGF0ZShuID0gbG9nKG4gKyAxKSAqIDEwMCkgJT4lIAogICAgICAgIG11dGF0ZShuID0gcm91bmQobiwgNCkpCgppZGZfMTBfdjIgPC0gdGZfaWRmX3YyICU+JQogICAgICAgICAgZ3JvdXBfYnkobWVkaW8pICU+JQogICAgICAgICAgYXJyYW5nZShkZXNjKGlkZikpICU+JQogICAgICAgICAgc2xpY2VfaGVhZChuID0gMTApICU+JQogICAgICAgICAgc2VsZWN0KG1lZGlvLCB3b3JkLCBpZGYpICU+JQogICAgICAgICAgcmVuYW1lKG4gPSBpZGYpICU+JQogICAgICAgICAgbXV0YXRlKG4gPSBsb2cobiArIDEpICogMTAwKSAlPiUgCiAgICAgICAgICBtdXRhdGUobiA9IHJvdW5kKG4sIDQpKQoKdGZfaWRmXzEwX3YyIDwtIHRmX2lkZl92MiAlPiUKICAgICAgICBncm91cF9ieShtZWRpbykgJT4lCiAgICAgICAgYXJyYW5nZShkZXNjKGlkZikpICU+JQogICAgICAgIHNsaWNlX2hlYWQobiA9IDEwKSAlPiUgCiAgICAgICAgc2VsZWN0KG1lZGlvLCB3b3JkLCB0Zl9pZGYpICU+JSAKICAgICAgICByZW5hbWUobiA9IHRmX2lkZikgJT4lCiAgICAgICAgbXV0YXRlKG4gPSBsb2cobiArIDEpICogMTAwKSAlPiUgCiAgICAgICAgbXV0YXRlKG4gPSByb3VuZChuLCA0KSkKYGBgCgpgYGB7ciB0Zl92aXpfdjJ9CmNyZWFyX2dyYWZfd29yZHModGZfMTBfdjIpIAoKY3JlYXJfZ3JhZl93b3Jkcyh0Zl8xMCkKYGBgCgpgYGB7ciBpZGZfdml6X3YyfQpjcmVhcl9ncmFmX3dvcmRzKGlkZl8xMF92MikKYGBgCgpgYGB7ciB0Zl9pZGZfdml6X3YyfQpjcmVhcl9ncmFmX3dvcmRzKHRmX2lkZl8xMF92MikKYGBgCgpBbsOhbGlzaXMgZmluYWwgZGUgVEYtSURGOgoKLSAgIEVuIHRvZG9zIGxvcyBtZWRpb3MsIGV4Y2VwdG8gZW4gTGEgTmFjacOzbiBxdWUgbm8gbGEgaW5jbHXDrWEsIGxvZ3LDsyBlbGltaW5hcnNlIGxhIHBhbGFicmEgImFuaW8iIHF1ZSBmaWd1cmFiYSBlbiBlbCBwcmltZXIgbHVnYXIgZW4gdMOpcm1pbm9zIGRlIGZyZWN1ZW5jaWEuIEFkZW3DoXMsIGVuIGxvcyBjYXNvcyBkZSBDcsOzbmljYSB5IE1pbnV0byAxLCBzZSBlbGltaW7DsyBvdHJhIHBhbGFicmEgYWRpY2lvbmFsIGluY29ycG9yYW5kbywgZGUgZXNhIG1hbmVyYSwgZG9zIG51ZXZhcyBhbCB0b3AgZGllei4gCgotIEVsIGNhc28gZGUgTGEgTmFjacOzbiB5YSBzZSBtdWVzdHJhIG5vcm1hbGl6YWRvLCBzaW4gbGFzIHN0b3B3b3JkcyBxdWUgYXBhcmVjw61hbiBwcm9kdWN0byBkZWwgc2NyYXBpbmcuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyMgTW9kZWxhZG8gZGUgdMOzcGljb3M6IExEQQoKwr9DdcOhbGVzIHNvbiBsb3MgdMOzcGljb3MgcHJpbmNpcGFsZXMgZW4gZWwgY29ycHVzPyDCv1B1ZWRlbiBldmlkZW5jaWFyIGRpZmVyZW5jaWFzIGVuIGNhZGEgdW5vIGRlIGxvcyBtZWRpb3M/IEV4cGxpY2FyIHF1w6kgbcOpdG9kbyBzZSB1dGlsaXrDsyBwYXJhIHJlc3BvbmRlciBsYSBwcmVndW50YSwgY3XDoWxlcyBzb24gbG9zIHN1cHVlc3RvcyBkZWwgbWlzbW8uIEdlbmVyYXIgbGFzIHZpc3VhbGl6YWNpb25lcyBtw6FzIGFkZWN1YWRhcyBwYXJhIHJlc3BvbmRlciBhIGxhcyBwcmVndW50YXMuCgpQYXJhIGltcGxlbWVudGFyIHVuIG1vZGVsYWRvIGRlIHRlbWFzIGNvbiBMREEgbmVjZXNpdGFtb3MgY29uc3RydWlyIHVuYSBtYXRyaXogRFRNLgoKIyBBIFBBUlRJUiBERSBBQ8OBCmRlam8gbGFzIGRvcyBwcnVlYmFzIHBhcmEgcXVlIHB1ZWRhcyBjb21wYXJhci4gU2kgdXNhbW9zIGxhIG1hdHJpeiB0w6lybWluby1mcmVjdWVuY2lhIHBvciBNRURJTywgbGEgZGV0ZWNjacOzbiBkZSB0w7NwaWNvcyBkYSBjdWFscXVpZXIgY29zYS4gU2kgdXNhbW9zIGxhIG1hdHJpeiB0w6lybWluby1mcmVjdWVuY2lhIHBvciBOT1RJQ0lBIGVsIHJlc3VsdGFkbyB0aWVuZSBtw6FzIHNlbnRpZG8uIE5vIGVzdG95IHNlZ3VyYSBjb21vIHNlIGV4cGxpY2EsIHF1aXrDoXMgcG9kZW1vcyBlbmNvbnRyYXIgYWxndW5hIGp1c3RpZmljYWNpw7NuLiAKCi0tPiBOb3MgcXVlZGFtb3MgY29uIGxhIHZlcnNpw7NuIDIgKHBvciBpZCkKCmBgYHtyIGR0bV8xfQpkaXNjX2R0bV8xIDwtIHdvcmRzX3RpZHlfdjIgJT4lCiAgICAgICAgICAgICAgICBjYXN0X2R0bShtZWRpbywgd29yZCwgbikKCmBgYAoKCmBgYHtyIGR0bV8yfQpwYXJhX2Rpc2NfZHRtIDwtIGNvcnB1c190aWR5X3YyICU+JSAKICBncm91cF9ieShpZCwgd29yZCkgJT4lCiAgc3VtbWFyaXNlKG49bigpKQojIGNyZW8gdW4gY29udGVvIGRlIHBhbGFicmFzIHBvciBub3RpY2lhLCBubyBwb3IgbWVkaW8gCgpkaXNjX2R0bV8yIDwtIHBhcmFfZGlzY19kdG0gJT4lCiAgICAgICAgICAgICAgICBjYXN0X2R0bShpZCwgd29yZCwgbikKCmBgYAoKYGBge3IgTERBX29yaWdpbmFsfQpsZGFfb3JpZ181IDwtIExEQShkaXNjX2R0bV8xLCBrID0gNSwgY29udHJvbCA9IGxpc3Qoc2VlZCA9IDEyMzQpKSAgIyBrIGVzIGVsIG7Dum1lcm8gZGUgdMOzcGljb3MgYSBpZGVudGlmaWNhcgoKYXBfdG9waWNzIDwtIHRpZHkobGRhX29yaWdfNSwgbWF0cml4ID0gImJldGEiKSAKIyBsYSBmdW5jacOzbiB0aWR5IGNvbnZpZXJ0ZSBlbCBtb2RlbG8gYSB1biB0w7NwaWNvLXTDqXJtaW5vIHBvciBmaWxhCgphcF90b3BpY3MgJT4lCiAgbXV0YXRlKGJldGEgPSByb3VuZCgxMDAqYmV0YSw2KSkKCmFwX3RvcF90ZXJtcyA8LSBhcF90b3BpY3MgJT4lCiAgZ3JvdXBfYnkodG9waWMpICU+JQogIHNsaWNlX21heChiZXRhLCBuID0gMTUpICU+JSAKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZSh0b3BpYywgLWJldGEpCgphcF90b3BfdGVybXMgJT4lCiAgbXV0YXRlKHRlcm0gPSByZW9yZGVyX3dpdGhpbih0ZXJtLCBiZXRhLCB0b3BpYykpICU+JQogIGdncGxvdChhZXMoYmV0YSwgdGVybSwgZmlsbCA9IGZhY3Rvcih0b3BpYykpKSArCiAgZ2VvbV9jb2woc2hvdy5sZWdlbmQgPSBGQUxTRSkgKwogIGZhY2V0X3dyYXAofiB0b3BpYywgc2NhbGVzPSdmcmVlX3knKSArCiAgc2NhbGVfeV9yZW9yZGVyZWQoKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKYGBge3J9CmxkYV81X3YyIDwtIExEQShkaXNjX2R0bV8yLCBrID0gMTAsIGNvbnRyb2wgPSBsaXN0KHNlZWQgPSAxMCkpICAjIGsgZXMgZWwgbsO6bWVybyBkZSB0w7NwaWNvcyBhIGlkZW50aWZpY2FyCgphcF90b3BpY3NfMiA8LSB0aWR5KGxkYV81X3YyLCBtYXRyaXggPSAiYmV0YSIpIAojIGxhIGZ1bmNpw7NuIHRpZHkgY29udmllcnRlIGVsIG1vZGVsbyBhIHVuIHTDs3BpY28tdMOpcm1pbm8gcG9yIGZpbGEKCmFwX3RvcGljc18yICU+JQogIG11dGF0ZShiZXRhID0gcm91bmQoMTAwKmJldGEsNikpCgphcF90b3BfdGVybXNfMiA8LSBhcF90b3BpY3NfMiAlPiUKICBncm91cF9ieSh0b3BpYykgJT4lCiAgc2xpY2VfbWF4KGJldGEsIG4gPSAxNSkgJT4lIAogIHVuZ3JvdXAoKSAlPiUKICBhcnJhbmdlKHRvcGljLCAtYmV0YSkKCmFwX3RvcF90ZXJtc18yICU+JQogIG11dGF0ZSh0ZXJtID0gcmVvcmRlcl93aXRoaW4odGVybSwgYmV0YSwgdG9waWMpKSAlPiUKICBnZ3Bsb3QoYWVzKGJldGEsIHRlcm0sIGZpbGwgPSBmYWN0b3IodG9waWMpKSkgKwogIGdlb21fY29sKHNob3cubGVnZW5kID0gRkFMU0UpICsKICBmYWNldF93cmFwKH4gdG9waWMsIHNjYWxlcz0nZnJlZV95JykgKwogIHNjYWxlX3lfcmVvcmRlcmVkKCkgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCgpQYXJlY2llcmEgcXVlIGxvcyBjaW5jbyB0w7NwaWNvcyBubyB0aWVuZW4gdW4gc2VudGlkbyB0YW4gZGVmaW5pZG8uIEVudG9uY2VzLCBjYWJlIGxhIHBvc2liaWxpZGFkIGRlIHF1ZSBoYWdhIGZhbHRhIHV0aWxpemFyIHVuIG7Dum1lcm8gZGUgdMOzcGljb3MgbcOhcyBlbGV2YWRvLgoKQSBzdSB2ZXosIGxhIHZpc3VhbGl6YWNpw7NuIGFudGVyaW9yIG11ZXN0cmEgcXVlIGFsZ3VuYXMgcGFsYWJyYXMgY29tby4uLiBzb24gY29tdW5lcyBhIG3DoXMgZGUgdW4gdGVtYS4gRXMgZGVjaXIsIHF1ZSBsb3MgdMOzcGljb3MgaWRlbnRpZmljYWRvcyB0aWVuZW4gY2llcnRhIHN1cGVycG9zaWNpw7NuIGVuIHTDqXJtaW5vcyBkZSBwYWxhYnJhcy5Db21vIGFsdGVybmF0aXZhLCBwb2Ryw61hbW9zIGNvbnNpZGVyYXIgbG9zIHTDqXJtaW5vcyBxdWUgdHV2aWVyYW4gbGEgbWF5b3IgZGlmZXJlbmNpYSBlbiDOsiBlbnRyZSB0b2RvcyBsb3MgdGVtYXMuCgpgYGB7cn0KCiMgRVNUTyBOTyBTw4kgU0kgRVNUw4EgQklFTiBBUExJQ0FETywgRU4gTEEgTk9URUJPT0sgRUwgQ0FTTyBFUkEgQUxHTyBESVNUSU5UTy4gU0UgQ0FMQ1VMQUJBIFNPQlJFIExPUyBUw5NQSUNPUyBNw4FTIERFRklOSURPUywgUEVSTyBFTiBOVUVTVFJPIENBU08gTklOR1VOTyBFUyBERUZJTklETy4KCiMgQ29uIGVzdGUgY8OzZGlnbyBjYWxjdWxhbW9zIGVsIGxvZ2FyaXRtbyBkZSBsYSBwcm9wb3JjacOzbiBkZSBjYWRhIHTDs3BpY28gcmVzcGVjdG8gYWwgdMOzcGljbyBkZSByZWZlcmVuY2lhIHBhcmEgbG9zIGNpbmNvIHTDs3BpY29zIGRlbCBjb25qdW50byBkZSBkYXRvcy4gQ2FkYSBjb2x1bW5hIGxvZ19yYXRpbyByZXByZXNlbnRhIGxhIGRpZmVyZW5jaWEgbG9nYXLDrXRtaWNhIGVudHJlIGxvcyB0w7NwaWNvcyBhZHlhY2VudGVzLiBFc3RvIHByb3BvcmNpb25hIGluZm9ybWFjacOzbiBzb2JyZSBjw7NtbyBjYW1iaWEgbGEgY29udHJpYnVjacOzbiByZWxhdGl2YSBkZSBjYWRhIHTDs3BpY28gZW4gY29tcGFyYWNpw7NuIGNvbiBzdSB0w7NwaWNvIGFkeWFjZW50ZS4KCmJldGFfd2lkZTMgPC0gYXBfdG9waWNzICU+JQogIG11dGF0ZSh0b3BpYyA9IHBhc3RlMCgidG9waWMiLCB0b3BpYykpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSB0b3BpYywgdmFsdWVzX2Zyb20gPSBiZXRhKSAlPiUKICBmaWx0ZXIodG9waWMxID4gMC4wMDIgfCB0b3BpYzIgPiAwLjAwMiB8IHRvcGljMyA+IDAuMDAyIHwgdG9waWM0ID4gMC4wMDIgfCB0b3BpYzUgPiAwLjAwMikgJT4lICAjIEZpbHRybyBwYXJhIGVsaW1pbmFyIHZhbG9yZXMgbXV5IGJham9zCiAgbXV0YXRlKGxvZ19yYXRpbzFfMiA9IGxvZzIodG9waWMyIC8gdG9waWMxKSwKICAgICAgICAgbG9nX3JhdGlvMl8zID0gbG9nMih0b3BpYzMgLyB0b3BpYzIpLAogICAgICAgICBsb2dfcmF0aW8zXzQgPSBsb2cyKHRvcGljNCAvIHRvcGljMyksCiAgICAgICAgIGxvZ19yYXRpbzRfNSA9IGxvZzIodG9waWM1IC8gdG9waWM0KSwKICAgICAgICAgbG9nX3JhdGlvNV8xID0gbG9nMih0b3BpYzEgLyB0b3BpYzUpKQoKYmV0YV93aWRlMwpgYGAKCmBgYHtyfQpiZXRhX3dpZGUzICU+JQogIGdncGxvdChhZXMoeD1yZW9yZGVyKHRlcm0sbG9nX3JhdGlvMV8yKSAsIHk9bG9nX3JhdGlvMV8yKSkgKwogICAgZ2VvbV9jb2woKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgbGFicyh4PSdUw6lybWlubycsCiAgICAgICAgIHk9J0xvZzIgcmF0aW8gdG9waWMzL3RvcGljMicpICsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCmBgYHtyfQpiZXRhX3dpZGUzICU+JQogIGdncGxvdChhZXMoeD1yZW9yZGVyKHRlcm0sbG9nX3JhdGlvMl8zKSAsIHk9bG9nX3JhdGlvMl8zKSkgKwogICAgZ2VvbV9jb2woKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgbGFicyh4PSdUw6lybWlubycsCiAgICAgICAgIHk9J0xvZzIgcmF0aW8gdG9waWMyL3RvcGljMScpICsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCmBgYHtyfQpiZXRhX3dpZGUzICU+JQogIGdncGxvdChhZXMoeD1yZW9yZGVyKHRlcm0sbG9nX3JhdGlvM180KSAsIHk9bG9nX3JhdGlvM180KSkgKwogICAgZ2VvbV9jb2woKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgbGFicyh4PSdUw6lybWlubycsCiAgICAgICAgIHk9J0xvZzIgcmF0aW8gdG9waWM0L3RvcGljMycpICsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCmBgYHtyfQpiZXRhX3dpZGUzICU+JQogIGdncGxvdChhZXMoeD1yZW9yZGVyKHRlcm0sbG9nX3JhdGlvNF81KSAsIHk9bG9nX3JhdGlvNF81KSkgKwogICAgZ2VvbV9jb2woKSArCiAgICBjb29yZF9mbGlwKCkgKwogICAgbGFicyh4PSdUw6lybWlubycsCiAgICAgICAgIHk9J0xvZzIgcmF0aW8gdG9waWM1L3RvcGljNCcpICsKICAgIHRoZW1lX21pbmltYWwoKQpgYGAKCmBgYHtyfQpiZXRhX3dpZGUzICU+JQogIGdncGxvdChhZXMoeD1yZW9yZGVyKHRlcm0sIGxvZ19yYXRpbzVfMSkgLCB5PSBsb2dfcmF0aW81XzEpKSArCiAgICBnZW9tX2NvbCgpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBsYWJzKHg9J1TDqXJtaW5vJywKICAgICAgICAgeT0nTG9nMiByYXRpbyB0b3BpYzEvdG9waWM1JykgKwogICAgdGhlbWVfbWluaW1hbCgpCmBgYAoKIyMgTW9kZWxhZG8gZGUgdMOzcGljb3M6IFNUTQoKUGFyYSBpbXBsZW1lbnRhciB1biBtb2RlbGFkbyBkZSB0ZW1hcyBjb24gU1RNIG5lY2VzaXRhbW9zIGNvbnN0cnVpciB1bmEgbWF0cml6IERGTS4KYGBge3IgZGZtfQpkaXNjX2RmbSA8LSB3b3Jkc190aWR5X3YyICU+JQogICAgICAgICAgICAgICAgY2FzdF9kZm0obWVkaW8sIHdvcmQsIG4pCgpkaXNjX2RmbQpgYGAKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCkEgY29udGludWFjacOzbiwgc2VsZWNjaW9uYXIgbGFzIG5vdGljaWFzIHZpbmN1bGFkYXMgYSBhbGfDum4gdMOzcGljbwpyZWxldmFudGUgKHBvciBlamVtcGxvLCDigJxFbGVjY2lvbmVz4oCdKSB5IGNvbnN0cnVpciB1biBjbGFzaWZpY2Fkb3IgcGFyYQpwcmVkZWNpciBsYSBvcmllbnRhY2nDs24gZGVsIGRpYXJpby4gVXRpbGl6YXIgYWxndW5vIGRlIGxvcyBtb2RlbG9zIGRlCmNsYXNpZmljYWNpw7NuIHZpc3RvcyBhIGxvIGxhcmdvIGRlIGFsIERpcGxvbWF0dXJhIChyZWdyZXNpw7NuIGxvZ8Otc3RpY2EsCnJhbmRvbSBmb3Jlc3QsIGV0Yy4pLiBVdGlsaXphciBjb21vIGZlYXR1cmVzIGVsIOKAnFNwYW5pc2ggQmlsbGlvbiBXb3JkCkNvcnB1cyBhbmQgRW1iZWRkaW5nc+KAnSwgYW5hbGl6YWRvIGVuIGNsYXNlIChwdWVkZW4gZGVzY2FyZ2FyIGVsCmVtYmVkZGluZyBlbiBmb3JtYXRvIC5iaW4gZGVsIGxpbmspLiDCv1F1w6kgcmVzdWx0YWRvcyBhcnJvamEgZWwgbW9kZWxvPwrCv0VzIHBvc2libGUgbWVkaWFudGUgZWwgdGV4dG8gZGUgbGFzIG5vdGljaWFzIGNvbm9jZXIgbGEgbMOtbmVhIGVkaXRvcmlhbApkZWwgZGlhcmlvPyBHZW5lcmFyIGxhcyB2aXN1YWxpemFjaW9uZXMgeSB0YWJsYXMgY29ycmVzcG9uZGllbnRlcyBwYXJhCnVuYSBjb3JyZWN0YSBldmFsdWFjacOzbiBkZWwgbW9kZWxvLgo=